home *** CD-ROM | disk | FTP | other *** search
- /*
- * builtin.c - Builtin functions and various utility procedures
- */
-
- /* Copyright © 1986, 1988, 1989 1991 the Free Software Foundation, Inc.
- * This file is part of GAWK, the GNU implementation of the
- * AWK Progamming Language, modified for the Macintosh (also called hAWK).
- * GAWK is free software; you can redistribute or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 1, or any later version.
- * GAWK is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with GAWK; see the file "COPYING hAWK". If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * Modified for THINK C 4 on the Macintosh by Ken Earle (Dynabyte) Aug 1991.
- */
-
- #include "AWK.H"
-
- extern short srandom(unsigned x );
- extern char *initstate(unsigned seed, char *arg_state, short n );
- extern char *setstate(char *arg_state );
- extern long random(void);
-
- extern char *gcvt(double value, short digits, char *buff);
-
- /* Mac version only: extension function to diagnose C token-type
- of user-defined top level terms. Used in do_lookup() below. */
- short InDictionary(char *tokenName);
-
- extern NODE **fields_arr;
-
- /* BUILTIN.C */
- NODE *do_exp(NODE *tree);
- NODE *do_index(NODE *tree);
- NODE *do_int(NODE *tree);
- NODE *do_length(NODE *tree);
- NODE *do_lookup(NODE *tree);
- NODE *do_log(NODE *tree);
- NODE *do_sprintf(NODE *tree);
- void do_printf(NODE *tree);
- NODE *do_sqrt(NODE *tree);
- NODE *do_substr(NODE *tree);
- NODE *do_system(NODE *tree);
- void do_print(register NODE *tree);
- NODE *do_tolower(NODE *tree);
- NODE *do_toupper(NODE *tree);
- static void get_one(NODE *tree, NODE **res);
- static void get_two(NODE *tree, NODE **res1, NODE **res2);
- static short get_three(NODE *tree, NODE **res1, NODE **res2, NODE **res3);
- short a_get_three(NODE *tree, NODE **res1, NODE **res2, NODE **res3);
- void print_simple(NODE *tree, FILE *fp);
- NODE *do_atan2(NODE *tree);
- NODE *do_sin(NODE *tree);
- NODE *do_cos(NODE *tree);
- NODE *do_rand(NODE *tree);
- NODE *do_srand(NODE *tree);
- NODE *do_match(NODE *tree);
- static NODE *sub_common(NODE *tree, short global);
- NODE *do_gsub(NODE *tree);
- NODE *do_sub(NODE *tree);
-
- /* Builtin functions */
- NODE *do_exp(NODE *tree)
- {
- NODE *tmp;
- double d, res;
- /*double exp();*/
-
- get_one(tree, &tmp);
- d = force_number(tmp);
- free_temp(tmp);
- errno = 0;
- res = exp(d);
- if (errno == ERANGE)
- warning("exp argument %g is out of range", d);
- return tmp_number((AWKNUM) res);
- }
-
- NODE *do_index(NODE *tree)
- {
- NODE *s1, *s2;
- register char *p1, *p2;
- register short l1, l2;
- long ret;
-
-
- get_two(tree, &s1, &s2);
- force_string(s1);
- force_string(s2);
- p1 = s1->stptr;
- p2 = s2->stptr;
- l1 = s1->stlen;
- l2 = s2->stlen;
- ret = 0;
- if (/*! strict && */ IGNORECASE_node->var_value->numbr != 0.0) {
- while (l1) {
- if (casetable[*p1] == casetable[*p2]
- && strncasecmp(p1, p2, l2) == 0) {
- ret = 1 + s1->stlen - l1;
- break;
- }
- l1--;
- p1++;
- }
- } else {
- while (l1) {
- if (STREQN(p1, p2, l2)) {
- ret = 1 + s1->stlen - l1;
- break;
- }
- l1--;
- p1++;
- }
- }
- free_temp(s1);
- free_temp(s2);
- return tmp_number((AWKNUM) ret);
- }
-
- NODE *do_int(NODE *tree)
- {
- NODE *tmp;
- /*double floor();*/
- double d;
-
- get_one(tree, &tmp);
- d = floor((double)force_number(tmp));
- free_temp(tmp);
- return tmp_number((AWKNUM) d);
- }
-
- NODE *do_length(NODE *tree)
- {
- NODE *tmp;
- short len;
-
- get_one(tree, &tmp);
- len = force_string(tmp)->stlen;
- free_temp(tmp);
- return tmp_number((AWKNUM) len);
- }
-
- NODE *do_lookup(NODE *tree)
- {
- NODE *tmp;
- short result;
-
- get_one(tree, &tmp);
- force_string(tmp);
- result = InDictionary(tmp->stptr);
- free_temp(tmp);
- return tmp_number((AWKNUM) result);
- }
-
- NODE *do_log(NODE *tree)
- {
- NODE *tmp;
- /*double log();*/
- double d, arg;
-
- get_one(tree, &tmp);
- arg = (double) force_number(tmp);
- if (arg < 0.0)
- warning("log called with negative argument %g", arg);
- d = log(arg);
- free_temp(tmp);
- return tmp_number((AWKNUM) d);
- }
-
- /*
- * Note that the output buffer cannot be static because sprintf may get
- * called recursively by force_string. Hence the wasteful alloca calls
- */
-
- /* %e and %f formats are not properly implemented. Someone should fix them */
- NODE *do_sprintf(NODE *tree)
- {
- #define bchunk(s,l) if(l) {\
- while((l)>ofre) {\
- char *tmp;\
- tmp=(char *)alloca(osiz*2);\
- memcpy(tmp,obuf,olen);\
- obuf=tmp;\
- ofre+=osiz;\
- osiz*=2;\
- }\
- memcpy(obuf+olen,s,(l));\
- olen+=(l);\
- ofre-=(l);\
- }
-
- /* Is there space for something L big in the buffer? */
- #define chksize(l) if((l)>ofre) {\
- char *tmp;\
- tmp=(char *)alloca(osiz*2);\
- memcpy(tmp,obuf,olen);\
- obuf=tmp;\
- ofre+=osiz;\
- osiz*=2;\
- }
-
- /*
- * Get the next arg to be formatted. If we've run out of args,
- * return "" (Null string)
- */
- #define parse_next_arg() {\
- if(!carg) arg= Nnull_string;\
- else {\
- get_one(carg,&arg);\
- carg=carg->rnode;\
- }\
- }
-
- char *obuf;
- short osiz, ofre, olen;
- static char chbuf[] = "0123456789abcdef";
- static char sp[] = " ";
- char *s0, *s1;
- short n0;
- NODE *sfmt, *arg;
- register NODE *carg;
- long fw, prec, lj, alt, big;
- long *cur;
- long val;
- #ifdef sun386 /* Can't cast unsigned (short/long) from ptr->value */
- long tmp_uval; /* on 386i 4.0.1 C compiler -- it just hangs */
- #endif
- unsigned long uval;
- short sgn;
- short base;
- char cpbuf[30]; /* if we have numbers bigger than 30 */
- char *cend = &cpbuf[30];/* chars, we lose, but seems unlikely */
- char *cp;
- char *fill;
- double tmpval;
- char *pr_str;
- short ucasehex = 0;
- extern char *gcvt(double value, short digits, char *buff);
-
-
- obuf = (char *) alloca(120);
- osiz = 120;
- ofre = osiz;
- olen = 0;
- get_one(tree, &sfmt);
- sfmt = force_string(sfmt);
- carg = tree->rnode;
- for (s0 = s1 = sfmt->stptr, n0 = sfmt->stlen; n0-- > 0;) {
- if (*s1 != '%') {
- s1++;
- continue;
- }
- bchunk(s0, (short)(s1 - s0));
- s0 = s1;
- cur = &fw;
- fw = 0;
- prec = 0;
- lj = alt = big = 0;
- fill = sp;
- cp = cend;
- s1++;
-
- retry:
- --n0;
- switch (*s1++) {
- case '%':
- bchunk("%", 1);
- s0 = s1;
- break;
-
- case '0':
- if (fill != sp || lj)
- goto lose;
- if (cur == &fw)
- fill = "0"; /* FALL through */
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (cur == 0)
- goto lose;
- *cur = s1[-1] - '0';
- while (n0 > 0 && *s1 >= '0' && *s1 <= '9') {
- --n0;
- *cur = *cur * 10 + *s1++ - '0';
- }
- goto retry;
- #ifdef not_yet
- case ' ': /* print ' ' or '-' */
- case '+': /* print '+' or '-' */
- #endif
- case '-':
- if (lj || fill != sp)
- goto lose;
- lj++;
- goto retry;
- case '.':
- if (cur != &fw)
- goto lose;
- cur = ≺
- goto retry;
- case '#':
- if (alt)
- goto lose;
- alt++;
- goto retry;
- case 'l':
- if (big)
- goto lose;
- big++;
- goto retry;
- case 'c':
- parse_next_arg();
- if (arg->flags & NUMERIC) {
- #ifdef sun386
- tmp_uval = arg->numbr;
- uval= (unsigned long) tmp_uval;
- #else
- uval = (unsigned long) arg->numbr;
- #endif
- cpbuf[0] = uval;
- prec = 1;
- pr_str = cpbuf;
- goto dopr_string;
- }
- if (! prec)
- prec = 1;
- else if (prec > arg->stlen)
- prec = arg->stlen;
- pr_str = arg->stptr;
- goto dopr_string;
- case 's':
- parse_next_arg();
- arg = force_string(arg);
- if (!prec || prec > arg->stlen)
- prec = arg->stlen;
- pr_str = arg->stptr;
-
- dopr_string:
- if (fw > prec && !lj) {
- while (fw > prec) {
- bchunk(sp, 1);
- fw--;
- }
- }
- bchunk(pr_str, (short) prec);
- if (fw > prec) {
- while (fw > prec) {
- bchunk(sp, 1);
- fw--;
- }
- }
- s0 = s1;
- free_temp(arg);
- break;
- case 'd':
- case 'i':
- parse_next_arg();
- val = (long) force_number(arg);
- free_temp(arg);
- if (val < 0) {
- sgn = 1;
- val = -val;
- } else
- sgn = 0;
- do {
- *--cp = '0' + val % 10;
- val /= 10;
- } while (val);
- if (sgn)
- *--cp = '-';
- if (prec > fw)
- fw = prec;
- prec = cend - cp;
- if (fw > prec && !lj) {
- if (fill != sp && *cp == '-') {
- bchunk(cp, 1);
- cp++;
- prec--;
- fw--;
- }
- while (fw > prec) {
- bchunk(fill, 1);
- fw--;
- }
- }
- bchunk(cp, (short) prec);
- if (fw > prec) {
- while (fw > prec) {
- bchunk(fill, 1);
- fw--;
- }
- }
- s0 = s1;
- break;
- case 'u':
- base = 10;
- goto pr_unsigned;
- case 'o':
- base = 8;
- goto pr_unsigned;
- case 'X':
- ucasehex = 1;
- case 'x':
- base = 16;
- goto pr_unsigned;
- pr_unsigned:
- parse_next_arg();
- uval = (unsigned long) force_number(arg);
- free_temp(arg);
- do {
- *--cp = chbuf[uval % base];
- if (ucasehex && isalpha(*cp))
- *cp = toupper(*cp);
- uval /= base;
- } while (uval);
- if (alt && (base == 8 || base == 16)) {
- if (base == 16) {
- if (ucasehex)
- *--cp = 'X';
- else
- *--cp = 'x';
- }
- *--cp = '0';
- }
- prec = cend - cp;
- if (fw > prec && !lj) {
- while (fw > prec) {
- bchunk(fill, 1);
- fw--;
- }
- }
- bchunk(cp, (short) prec);
- if (fw > prec) {
- while (fw > prec) {
- bchunk(fill, 1);
- fw--;
- }
- }
- s0 = s1;
- break;
- case 'g':
- parse_next_arg();
- tmpval = force_number(arg);
- free_temp(arg);
- if (prec == 0)
- prec = 13;
- (void) gcvt(tmpval, (short) prec, cpbuf);
- prec = strlen(cpbuf);
- cp = cpbuf;
- if (fw > prec && !lj) {
- if (fill != sp && *cp == '-') {
- bchunk(cp, 1);
- cp++;
- prec--;
- } /* Deal with .5 as 0.5 */
- if (fill == sp && *cp == '.') {
- --fw;
- while (--fw >= prec) {
- bchunk(fill, 1);
- }
- bchunk("0", 1);
- } else
- while (fw-- > prec)
- bchunk(fill, 1);
- } else {/* Turn .5 into 0.5 */
- /* FOO */
- if (*cp == '.' && fill == sp) {
- bchunk("0", 1);
- --fw;
- }
- }
- bchunk(cp, (short) prec);
- if (fw > prec)
- while (fw-- > prec)
- bchunk(fill, 1);
- s0 = s1;
- break;
- case 'f':
- parse_next_arg();
- tmpval = force_number(arg);
- free_temp(arg);
- chksize(fw + prec + 5); /* 5==slop */
-
- cp = cpbuf;
- *cp++ = '%';
- if (lj)
- *cp++ = '-';
- if (fill != sp)
- *cp++ = '0';
- if (cur != &fw) {
- (void) strcpy(cp, "*.*f");
- (void) sprintf(obuf + olen, cpbuf, (short) fw, (short) prec, (double) tmpval);
- } else {
- (void) strcpy(cp, "*f");
- (void) sprintf(obuf + olen, cpbuf, (short) fw, (double) tmpval);
- }
- ofre -= strlen(obuf + olen);
- olen += strlen(obuf + olen); /* There may be nulls */
- s0 = s1;
- break;
- case 'e':
- parse_next_arg();
- tmpval = force_number(arg);
- free_temp(arg);
- chksize(fw + prec + 5); /* 5==slop */
- cp = cpbuf;
- *cp++ = '%';
- if (lj)
- *cp++ = '-';
- if (fill != sp)
- *cp++ = '0';
- if (cur != &fw) {
- (void) strcpy(cp, "*.*e");
- (void) sprintf(obuf + olen, cpbuf, (short) fw, (short) prec, (double) tmpval);
- } else {
- (void) strcpy(cp, "*e");
- (void) sprintf(obuf + olen, cpbuf, (short) fw, (double) tmpval);
- }
- ofre -= strlen(obuf + olen);
- olen += strlen(obuf + olen); /* There may be nulls */
- s0 = s1;
- break;
-
- default:
- lose:
- break;
- }
- }
- bchunk(s0, (short)(s1 - s0));
- free_temp(sfmt);
- return tmp_string(obuf, olen);
- }
-
- void do_printf(NODE *tree)
- {
- struct redirect *rp = NULL;
- register FILE *fp = stdout;
- short errflg = 0; /* not used, sigh */
-
- if (tree->rnode) {
- rp = redirect(tree->rnode, &errflg);
- if (rp)
- fp = rp->fp;
- }
- if (fp)
- print_simple(do_sprintf(tree->lnode), fp);
- if (rp && (rp->flag & RED_NOBUF))
- fflush(fp);
- }
-
- NODE *do_sqrt(NODE *tree)
- {
- NODE *tmp;
- /*double sqrt();*/
- double d, arg;
-
- get_one(tree, &tmp);
- arg = (double) force_number(tmp);
- if (arg < 0.0)
- warning("sqrt called with negative argument %g", arg);
- d = sqrt(arg);
- free_temp(tmp);
- return tmp_number((AWKNUM) d);
- }
-
- NODE *do_substr(NODE *tree)
- {
- NODE *t1, *t2, *t3;
- NODE *r;
- register short indx, length;
-
- t1 = t2 = t3 = NULL;
- length = -1;
- if (get_three(tree, &t1, &t2, &t3) == 3)
- length = (short) force_number(t3);
- indx = (short) force_number(t2) - 1;
- t1 = force_string(t1);
- if (length == -1)
- length = t1->stlen;
- if (indx < 0)
- indx = 0;
- if (indx >= t1->stlen || length <= 0) {
- if (t3)
- free_temp(t3);
- free_temp(t2);
- free_temp(t1);
- return Nnull_string;
- }
- if (indx + length > t1->stlen)
- length = t1->stlen - indx;
- if (t3)
- free_temp(t3);
- free_temp(t2);
- r = tmp_string(t1->stptr + indx, length);
- free_temp(t1);
- return r;
- }
-
- NODE *do_system(NODE *tree)
- {
- #if defined(unix) || defined(MSDOS) /* || defined(gnu) */
- NODE *tmp;
- short ret;
-
- (void) flush_io (); /* so output is synchronous with gawk's */
- get_one(tree, &tmp);
- ret = system(force_string(tmp)->stptr);
- ret = (ret >> 8) & 0xff;
- free_temp(tmp);
- return tmp_number((AWKNUM) ret);
- #else
- fatal("the \"system\" function is not supported.");
- /* NOTREACHED */
- #endif
- }
-
- void do_print(register NODE *tree)
- {
- struct redirect *rp = NULL;
- register FILE *fp = stdout;
- short errflg = 0; /* not used, sigh */
-
- if (tree->rnode) {
- rp = redirect(tree->rnode, &errflg);
- if (rp)
- fp = rp->fp;
- }
- if (!fp)
- return;
- tree = tree->lnode;
- if (!tree)
- tree = WHOLELINE;
- if (tree->type != Node_expression_list) {
- if (!(tree->flags & STR))
- cant_happen();
- print_simple(tree, fp);
- } else {
- while (tree) {
- print_simple(force_string(tree_eval(tree->lnode)), fp);
- tree = tree->rnode;
- if (tree)
- print_simple(OFS_node->var_value, fp);
- }
- }
- print_simple(ORS_node->var_value, fp);
- if (rp && (rp->flag & RED_NOBUF))
- fflush(fp);
- }
-
- NODE *do_tolower(NODE *tree)
- {
- NODE *t1, *t2;
- register char *cp, *cp2;
-
- get_one(tree, &t1);
- t1 = force_string(t1);
- t2 = tmp_string(t1->stptr, t1->stlen);
- for (cp = t2->stptr, cp2 = t2->stptr + t2->stlen; cp < cp2; cp++)
- if (isupper(*cp))
- *cp = tolower(*cp);
- free_temp(t1);
- return t2;
- }
-
- NODE *do_toupper(NODE *tree)
- {
- NODE *t1, *t2;
- register char *cp;
-
- get_one(tree, &t1);
- t1 = force_string(t1);
- t2 = tmp_string(t1->stptr, t1->stlen);
- for (cp = t2->stptr; cp < t2->stptr + t2->stlen; cp++)
- if (islower(*cp))
- *cp = toupper(*cp);
- free_temp(t1);
- return t2;
- }
-
- /*
- * Get the arguments to functions. No function cares if you give it too many
- * args (they're ignored). Only a few fuctions complain about being given
- * too few args. The rest have defaults.
- */
-
- static void get_one(NODE *tree, NODE **res)
- {
- if (!tree) {
- *res = WHOLELINE;
- return;
- }
- *res = tree_eval(tree->lnode);
- }
-
- static void get_two(NODE *tree, NODE **res1, NODE **res2)
- {
- if (!tree) {
- *res1 = WHOLELINE;
- return;
- }
- *res1 = tree_eval(tree->lnode);
- if (!tree->rnode)
- return;
- tree = tree->rnode;
- *res2 = tree_eval(tree->lnode);
- }
-
- static short get_three(NODE *tree, NODE **res1, NODE **res2, NODE **res3)
- {
- if (!tree) {
- *res1 = WHOLELINE;
- return 0;
- }
- *res1 = tree_eval(tree->lnode);
- if (!tree->rnode)
- return 1;
- tree = tree->rnode;
- *res2 = tree_eval(tree->lnode);
- if (!tree->rnode)
- return 2;
- tree = tree->rnode;
- *res3 = tree_eval(tree->lnode);
- return 3;
- }
-
- short a_get_three(NODE *tree, NODE **res1, NODE **res2, NODE **res3)
- {
- if (!tree) {
- *res1 = WHOLELINE;
- return 0;
- }
- *res1 = tree_eval(tree->lnode);
- if (!tree->rnode)
- return 1;
- tree = tree->rnode;
- *res2 = tree->lnode;
- if (!tree->rnode)
- return 2;
- tree = tree->rnode;
- *res3 = tree_eval(tree->lnode);
- return 3;
- }
-
- void print_simple(NODE *tree, FILE *fp)
- {
- if (fwrite(tree->stptr, sizeof(char), tree->stlen, fp) != tree->stlen)
- warning("fwrite: %s", strerror(errno));
- free_temp(tree);
- }
-
- NODE *do_atan2(NODE *tree)
- {
- NODE *t1, *t2;
- /*extern double atan2();*/
- double d1, d2;
-
- get_two(tree, &t1, &t2);
- d1 = force_number(t1);
- d2 = force_number(t2);
- free_temp(t1);
- free_temp(t2);
- return tmp_number((AWKNUM) atan2(d1, d2));
- }
-
- NODE *do_sin(NODE *tree)
- {
- NODE *tmp;
- /*extern double sin();*/
- double d;
-
- get_one(tree, &tmp);
- d = sin((double)force_number(tmp));
- free_temp(tmp);
- return tmp_number((AWKNUM) d);
- }
-
- NODE *do_cos(NODE *tree)
- {
- NODE *tmp;
- /*extern double cos();*/
- double d;
-
- get_one(tree, &tmp);
- d = cos((double)force_number(tmp));
- free_temp(tmp);
- return tmp_number((AWKNUM) d);
- }
-
- static short firstrand = 1;
- static char state[256];
-
- #define MAXLONG 2147483647 /* maximum value for long short */
-
- /* ARGSUSED */
- NODE *do_rand(NODE *tree)
- {
- if (firstrand) {
- (void) initstate((unsigned) 1, state, sizeof state);
- srandom(1);
- firstrand = 0;
- }
- return tmp_number((AWKNUM) random() / MAXLONG);
- }
-
- NODE *do_srand(NODE *tree)
- {
- NODE *tmp;
- static unsigned long save_seed = 1;
- long ret = save_seed; /* SVR4 awk srand returns previous seed */
- /*extern long time();*/
-
- if (firstrand)
- (void) initstate((unsigned) 1, state, sizeof state);
- else
- (void) setstate(state);
-
- if (!tree)
- srandom((short) (save_seed = time((unsigned long *) 0)));
- else {
- get_one(tree, &tmp);
- srandom((short) (save_seed = (long) force_number(tmp)));
- free_temp(tmp);
- }
- firstrand = 0;
- return tmp_number((AWKNUM) ret);
- }
-
- NODE *do_match(NODE *tree)
- {
- NODE *t1;
- short rstart;
- struct re_registers reregs;
- struct re_pattern_buffer *rp;
- short need_to_free = 0;
-
- t1 = force_string(tree_eval(tree->lnode));
- tree = tree->rnode;
- if (tree == NULL || tree->lnode == NULL)
- fatal("match called with only one argument");
- tree = tree->lnode;
- if (tree->type == Node_regex) {
- rp = tree->rereg;
- if (/*!strict && */ ((IGNORECASE_node->var_value->numbr != 0)
- ^ (tree->re_case != 0))) {
- /* recompile since case sensitivity differs */
- rp = tree->rereg =
- mk_re_parse(tree->re_text,
- (IGNORECASE_node->var_value->numbr != 0));
- tree->re_case =
- (IGNORECASE_node->var_value->numbr != 0);
- }
- } else {
- need_to_free = 1;
- rp = make_regexp(force_string(tree_eval(tree)),
- (IGNORECASE_node->var_value->numbr != 0));
- if (rp == NULL)
- cant_happen();
- }
- rstart = re_search(rp, t1->stptr, t1->stlen, 0, t1->stlen, &reregs);
- free_temp(t1);
- if (rstart >= 0) {
- rstart++; /* 1-based indexing */
- /* RSTART set to rstart below */
- assign_number(& RLENGTH_node->var_value,
- (AWKNUM) (reregs.end[0] - reregs.start[0]));
- } else {
- /*
- * Match failed. Set RSTART to 0, RLENGTH to -1.
- * Return the value of RSTART.
- */
- rstart = 0; /* used as return value */
- assign_number(& RLENGTH_node->var_value, (AWKNUM) -1.0);
- }
- assign_number(& RSTART_node->var_value, (AWKNUM) rstart);
- if (need_to_free) {
- free(rp->buffer);
- free(rp->fastmap);
- free((char *) rp);
- }
- return tmp_number((AWKNUM) rstart);
- }
-
-
- static NODE *sub_common(NODE *tree, short global)
- {
- register short len;
- register char *scan;
- register char *bp, *cp;
- short search_start = 0;
- short match_length;
- short matches = 0;
- char *buf;
- struct re_pattern_buffer *rp;
- NODE *s; /* subst. pattern */
- NODE *t; /* string to make sub. in; $0 if none given */
- struct re_registers reregs;
- unsigned short saveflags;
- NODE *tmp;
- NODE **lhs;
- char *lastbuf;
- short need_to_free = 0;
- char theTag;
- short regForTag;
-
- if (tree == NULL)
- fatal("sub or gsub called with 0 arguments");
- tmp = tree->lnode;
- if (tmp->type == Node_regex) {
- rp = tmp->rereg;
- if (/*! strict && */ ((IGNORECASE_node->var_value->numbr != 0)
- ^ (tmp->re_case != 0))) {
- /* recompile since case sensitivity differs */
- rp = tmp->rereg =
- mk_re_parse(tmp->re_text,
- (IGNORECASE_node->var_value->numbr != 0));
- tmp->re_case = (IGNORECASE_node->var_value->numbr != 0);
- }
- } else {
- need_to_free = 1;
-
- rp = make_regexp(force_string(tree_eval(tmp)),
- (IGNORECASE_node->var_value->numbr != 0));
- if (rp == NULL)
- cant_happen();
- }
- tree = tree->rnode;
- if (tree == NULL)
- fatal("sub or gsub called with only 1 argument");
- s = force_string(tree_eval(tree->lnode));
- tree = tree->rnode;
- deref = 0;
- field_num = -1;
- if (tree == NULL) {
- t = node0_valid ? fields_arr[0] : *get_field(0, 0);
- lhs = &fields_arr[0];
- field_num = 0;
- deref = t;
- } else {
-
- t = tree->lnode;
- lhs = get_lhs(t, 1);
- t = force_string(tree_eval(t));
- }
- /*
- * create a private copy of the string
- */
- if (t->stref > 1 || (t->flags & PERM)) {
- saveflags = t->flags;
- t->flags &= ~MALLOC; /* MALLOC is defined inside NODE struct */
- tmp = dupnode(t);
- t->flags = saveflags;
- do_deref();
- t = tmp;
- if (lhs)
- *lhs = tmp;
- }
- lastbuf = t->stptr;
- do {
- if (re_search(rp, t->stptr, t->stlen, search_start,
- t->stlen-search_start, &reregs) == -1
- || reregs.start[0] == reregs.end[0])
- break;
- matches++;
- /*
- * first, make a pass through the sub. pattern, to calculate
- * the length of the string after substitution
- */
- match_length = reregs.end[0] - reregs.start[0];
- len = t->stlen - match_length;
- for (scan = s->stptr; scan < s->stptr + s->stlen; scan++)
- if (*scan == '&')
- len += match_length;
- else if (*scan == '\\' && *(scan+1) == '&')
- {
- scan++;
- len++;
- }
- else if (*scan == '\\' && (theTag = *(scan+1)) >= '1'
- && theTag <= '9')
- {
- scan++;
- regForTag = theTag - '0';
- len += reregs.end[regForTag] - reregs.start[regForTag];
- }
- else
- len++;
- emalloc(buf, char *, len + 1, "do_sub");
- bp = buf;
-
- /*
- * now, create the result, copying in parts of the original
- * string
- */
- for (scan = t->stptr; scan < t->stptr + reregs.start[0]; scan++)
- *bp++ = *scan;
- for (scan = s->stptr; scan < s->stptr + s->stlen; scan++)
- if (*scan == '&')
- for (cp = t->stptr + reregs.start[0];
- cp < t->stptr + reregs.end[0]; cp++)
- *bp++ = *cp;
- else if (*scan == '\\' && *(scan+1) == '&')
- {
- scan++;
- *bp++ = *scan;
- }
- else if (*scan == '\\' && (theTag = *(scan+1)) >= '1'
- && theTag <= '9')
- {
- scan++;
- regForTag = theTag - '0';
- for (cp = t->stptr + reregs.start[regForTag];
- cp < t->stptr + reregs.end[regForTag]; cp++)
- *bp++ = *cp;
- }
- else
- *bp++ = *scan;
- search_start = bp - buf;
- for (scan = t->stptr + reregs.end[0];
- scan < t->stptr + t->stlen; scan++)
- *bp++ = *scan;
- *bp = '\0';
- free(lastbuf);
- t->stptr = buf;
- lastbuf = buf;
- t->stlen = len;
- } while (global && search_start < t->stlen);
-
- free_temp(s);
- if (need_to_free) {
- free(rp->buffer);
- free(rp->fastmap);
- free((char *) rp);
- }
- if (matches > 0) {
- if (field_num == 0)
- set_record(fields_arr[0]->stptr, fields_arr[0]->stlen);
- t->flags &= ~(NUM|NUMERIC);
- }
- field_num = -1;
- return tmp_number((AWKNUM) matches);
- }
-
- NODE *do_gsub(NODE *tree)
- {
- return sub_common(tree, 1);
- }
-
- NODE *do_sub(NODE *tree)
- {
- return sub_common(tree, 0);
- }
-
-